#pragma once
#include <iostream>
#include <sys/select.h>
#include <unistd.h>
#include "Sock.hpp"
#include <string>
using namespace std;

#define N (sizeof(fd_set) * 8)
#define READ_EVENT 0x1
#define WRITE_EVENT 0X2
#define EXCEPT_EVENT 0x4

typedef struct FdEvent
{
    int fd;
    int event;
    string ip;
    uint16_t port;
} type_t;

static const int defaultfd = -1;
static const int defaultevent = 0;
static const uint16_t defaultport = -1;
class SelectServer
{
public:
    SelectServer(uint16_t port = 8888)
        : _port(port)
    {
    }

    void Init()
    {
        _listensock.Socket();
        _listensock.Bind(_port);
        _listensock.Listen();
        for (int i = 0; i < N; i++)
        {
            _fdarray[i].fd = defaultfd;
            _fdarray[i].event = defaultevent;
            _fdarray[i].port = defaultport;
        }
    }

    void HandleAccept()
    {
        // 这里在进行Accept会不会被阻塞??不会的!
        string clientip;
        uint16_t clientport;
        int sock = _listensock.Accept(clientip, clientport);
        if (sock < 0)
        {
            return;
        }
        // 得到了对应的sock, 我们可不可以进行read/recv，读取sock？不能
        // 你怎么知道sock上有数据就绪了？不知道，所以我们需要将sock交给select，让select进行管理！
        LogMessage(Information, "[%s,%d],sock:%d", clientip.c_str(), clientport, sock);
        // 要让select 帮我们进行关心，只要把sock添加到fdarray_[]里面即可！
        int i;
        for (i = 0; i < N; i++)
        {
            if (_fdarray[i].fd == defaultfd)
            {
                break;
            }
        }
        if (i >= N)
        {
            LogMessage(Warning, "sock array have already full");
            close(sock);
        }
        else
        {
            _fdarray[i].fd = sock;
            _fdarray[i].event = READ_EVENT;
            _fdarray[i].ip = clientip;
            _fdarray[i].port = clientport;
        }
    }

    void HandleRead(int index)
    {
        // 读
        int fd = _fdarray[index].fd;
        char buff[1024];
        int n = read(fd, buff, sizeof(buff) - 1); // 读取会被阻塞吗？不会
        if (n > 0)
        {
            buff[n - 1] = 0;
            cout << "client echo:" << buff << endl;
            string send_to = buff;
            send_to += "[select server echo]";
            cout << send_to << endl;
            write(fd, send_to.c_str(), send_to.size());
        }
        else
        {
            if (n == 0)
            {
                LogMessage(Information, "client want to end...");
            }
            else
            {
                LogMessage(Warning, "read error,errno:%d,string:%s", errno, strerror(errno));
            }

            close(fd); // 不能忘记，remember
            _fdarray[index].fd = defaultfd;
            _fdarray[index].event = defaultevent;
            _fdarray[index].ip.resize(0);
            _fdarray[index].port = defaultport;
        }
    }

    void HandleSelect(fd_set &rfds, fd_set &wfds)
    {
        for (int i = 0; i < N; i++)
        {
            if (_fdarray[i].fd == defaultfd)
            {
                continue;
            }

            if ((_fdarray[i].event & READ_EVENT) && (FD_ISSET(_fdarray[i].fd, &rfds)))
            {
                // 处理读取，1. accept 2. recv
                if (_fdarray[i].fd == _listensock.GetSock())
                {
                    HandleAccept();
                }
                else if (_fdarray[i].fd != _listensock.GetSock())
                {
                    HandleRead(i);
                }
                else
                {
                }
            }
        }
    }

    void Start()
    {
        int maxfd;
        fd_set rfds;
        fd_set wfds;
        // 1. 这里我们能够直接获取新的链接吗？
        // 2. 最开始的时候，我们的服务器是没有太多的sock的，甚至只有一个sock！listensock
        // 3. 在网络中， 新连接到来被当做 读事件就绪！
        // listensock_.Accept(); 不能！
        while (true)
        {
            // 因为rfds是一个输入输出型参数，注定了每次都要对rfds进行重置，重置，必定要知道我历史上都有哪些fd?fdarray_[]
            // 因为服务器在运行中，sockfd的值一直在动态变化，所以maxfd也一定在变化， maxfd是不是也要进行动态更新， fdarray_[]
            FD_ZERO(&rfds);
            FD_ZERO(&wfds);
            _fdarray[0].fd = _listensock.GetSock();
            _fdarray[0].event = READ_EVENT;
            maxfd = _listensock.GetSock();
            for (int i = 0; i < N; i++) // 通过循环来重新添加要监视的文件描述符
            {

                if (_fdarray[i].fd == defaultfd)
                {
                    continue;
                }
                if (_fdarray[i].event & READ_EVENT) // 必须要用&,用==的话会造成问题，如果fd同时要监测读写
                {
                    FD_SET(_fdarray[i].fd, &rfds);
                }
                if (_fdarray[i].event & WRITE_EVENT) // 同理↑,不能用else if
                {
                    FD_SET(_fdarray[i].fd, &wfds);
                }

                if (maxfd < _fdarray[i].fd) // 选出要监视的最大的文件描述符
                {
                    maxfd = _fdarray[i].fd;
                }
            }
            int n = select(maxfd + 1, &rfds, &wfds, NULL, NULL);
            if (n == 0)
            {
                LogMessage(Information, "time is out...");
                continue;
            }
            else if (n < 0)
            {
                LogMessage(Warning, "select is error,errno:%d,string:%s", errno, strerror(errno));
                continue;
            }
            else
            {
                // 有文件描述符可读
                LogMessage(Debug, "There is at  %d fd(s) has already", n);
                HandleSelect(rfds, wfds);
                DebugPrint();
                continue;
            }
        }
    }
    void DebugPrint()
    {
        std::cout << "fdarray[]: ";
        for (int i = 0; i < N; i++)
        {
            if (_fdarray[i].fd == defaultfd)
                continue;
            std::cout << _fdarray[i].fd << " ";
        }
        std::cout << "\n";
    }

private:
    Sock _listensock;
    uint16_t _port;
    type_t _fdarray[N];
};